#include	"Net.h"

#define		NET_DATAMAXLEN	65536

static char * __net_unsafe_buffer = new char[NET_DATAMAXLEN * 2];

//////////////////////////////////////////////////////////////////////////
/// IMPLEMENT OF Sender
//////////////////////////////////////////////////////////////////////////
bool Sender::Reply(int pf, const google::protobuf::Message & msg, uint32_t serial /* = 0 */, bool threadsafe /* = false */) {
	size_t size = msg.ByteSizeLong();
	if (size + sizeof(Protocol) > NET_DATAMAXLEN) {
		LOG_ERR("Failed to send message '%s' to server due to size.", msg.GetTypeName().c_str());
		return false;
	}

	char * use = __net_unsafe_buffer;
	if (threadsafe) {
		use = new char[size + sizeof(Protocol)];
		if (!use) {
			LOG_ERR("Failed to send message '%s' to server due to alloc.", msg.GetTypeName().c_str());
			return false;
		}
	}

	Protocol * header = (Protocol *)use;
	header->pf = pf & 0xFFFF;
	header->size = (size + sizeof(Protocol)) & 0xFFFF;
	header->serial = serial;

	if (!msg.SerializeToArray(header->data, (int)size)) {
		LOG_ERR("Failed to send message '%s' to server due to serialize.", msg.GetTypeName().c_str());
		if (threadsafe) delete[] use;
		return false;
	}

	bool ret = _conn->Send(use, header->size);
	if (threadsafe) delete[] use;
	return ret;
}

//////////////////////////////////////////////////////////////////////////
/// IMPLEMENT OF IBaseConnector
//////////////////////////////////////////////////////////////////////////
IBaseConnector::IBaseConnector(const std::string & name)
	: _name(name)
	, _current(nullptr)
	, _arena_buffer(new char[NET_DATAMAXLEN * 2])
	, _arena(nullptr) {
	google::protobuf::ArenaOptions options;
	options.initial_block = _arena_buffer;
	options.initial_block_size = NET_DATAMAXLEN * 2;
	_arena = new google::protobuf::Arena(options);
}

IBaseConnector::~IBaseConnector() {
	_arena->Reset();
	delete _arena;
	delete[] _arena_buffer;
}

void IBaseConnector::OnReceive(char * data, size_t size) {
	size_t readed = 0;

	while (readed < size) {
		if (size - readed < sizeof(Protocol)) {
			LOG_ERR("[%s]Received broken message from server.", _name.c_str());
			break;
		}

		_current = (Protocol *)(data + readed);
		if (_current->size > size - readed) {
			LOG_ERR("[%s]Received broken message from server. second.", _name.c_str());
			break;
		}

		readed += _current->size;
		OnReceive(_current);
		_arena->Reset();
	}
}

bool IBaseConnector::Send(int pf, const google::protobuf::Message & msg, uint32_t serial /* = 0 */, bool threadsafe /* = false */) {
	size_t size = msg.ByteSizeLong();
	if (size + sizeof(Protocol) > NET_DATAMAXLEN) {
		LOG_ERR("[%s]Failed to send message '%s' to server due to size.", _name.c_str(), msg.GetTypeName().c_str());
		return false;
	}

	char * use = __net_unsafe_buffer;
	if (threadsafe) {
		use = new char[size + sizeof(Protocol)];
		if (!use) {
			LOG_ERR("[%s]Failed to send message '%s' to server due to alloc.", _name.c_str(), msg.GetTypeName().c_str());
			return false;
		}
	}

	Protocol * header = (Protocol *)use;
	header->pf = pf & 0xFFFF;
	header->size = (size + sizeof(Protocol)) & 0xFFFF;
	header->serial = serial;
	
	if (!msg.SerializeToArray(header->data, (int)size)) {
		LOG_ERR("[%s]Failed to send message '%s' to server due to serialize.", _name.c_str(), msg.GetTypeName().c_str());
		if (threadsafe) delete[] use;
		return false;
	}

	bool ret = IConnector::Send(use, header->size);
	if (threadsafe) delete[] use;
	return ret;
}

//////////////////////////////////////////////////////////////////////////
/// IMPLEMENT OF IBaseService.
//////////////////////////////////////////////////////////////////////////
IBaseService::IBaseService(const std::string & name)
	: _name(name)
	, _current(nullptr)
	, _arena_buffer(new char[NET_DATAMAXLEN * 2])
	, _arena(nullptr) {
	google::protobuf::ArenaOptions options;
	options.initial_block = _arena_buffer;
	options.initial_block_size = NET_DATAMAXLEN * 2;
	_arena = new google::protobuf::Arena(options);
}

IBaseService::~IBaseService() {
	_arena->Reset();
	delete _arena;
	delete[] _arena_buffer;
}

void IBaseService::OnAccept(Connection * conn) {
	LOG_INFO("[%s]Accept %s. Using id : %llu", _name.c_str(), conn->IP(), conn->Id());
	Sender * sender = new Sender(conn);
	OnAccept(sender);
}

void IBaseService::OnClose(Connection * conn, ENet::Close reason) {
	LOG_INFO("[%s]Disconnect %s. Free id : %llu. Reason : %d", _name.c_str(), conn->IP(), conn->Id(), reason);
	Sender * sender = conn->Attached<Sender>();
	OnClose(sender, reason);
	if (sender) delete sender;
}

void IBaseService::OnShutdown() {
	LOG_INFO("[%s]Shutdown", _name.c_str());
}

void IBaseService::OnReceive(Connection * conn, char * data, size_t size) {
	size_t readed = 0;

	while (readed < size) {
		if (size - readed < sizeof(Protocol)) {
			LOG_ERR("[%s]Received broken message from client(%llu).", _name.c_str(), conn->Id());
			break;
		}

		_current = (Protocol *)(data + readed);
		if (_current->size > size - readed) {
			LOG_ERR("[%s]Received broken message from client(%llu). second.", _name.c_str(), conn->Id());
			break;
		}

		readed += _current->size;
		OnReceive(conn->Attached<Sender>(), _current);
		_arena->Reset();
	}
}

Sender * IBaseService::Find(uint64_t id) {
	Connection * conn = IService::Find(id);
	if (!conn) return nullptr;
	return conn->Attached<Sender>();
}

void IBaseService::Broadcast(int pf, const google::protobuf::Message & msg, bool threadsafe /* = false */) {
	size_t size = msg.ByteSizeLong();
	if (size + sizeof(Protocol) > NET_DATAMAXLEN) {
		LOG_ERR("[%s]Failed to send message '%s' to server due to size.", _name.c_str(), msg.GetTypeName().c_str());
		return;
	}

	char * use = __net_unsafe_buffer;
	if (threadsafe) {
		use = new char[size + sizeof(Protocol)];
		if (!use) {
			LOG_ERR("[%s]Failed to send message '%s' to server due to alloc.", _name.c_str(), msg.GetTypeName().c_str());
			return;
		}
	}

	Protocol * header = (Protocol *)use;
	header->pf = pf & 0xFFFF;
	header->size = (size + sizeof(Protocol)) & 0xFFFF;
	header->serial = 0;

	if (!msg.SerializeToArray(header->data, (int)size)) {
		LOG_ERR("[%s]Failed to send message '%s' to server due to serialize.", _name.c_str(), msg.GetTypeName().c_str());
		if (threadsafe) delete[] use;
		return;
	}

	IService::Broadcast(use, header->size);
	if (threadsafe) delete[] use;
}